/***
 *  Platypus: Page Layout and Typesetting Software (free at platypus.pz.org)
 *
 *  Platypus is (c) Copyright 2009-12 Pacific Data Works LLC. All Rights Reserved.
 *  Licensed under Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0.html)
 */
package org.pz.platypus.commandline;

import org.pz.platypus.exceptions.StopExecutionException;
import org.apache.commons.cli.*;

import java.util.*;

/**
 * Parse the command line using Apache Commons CLI
 * @author alb
 * @author atul - moved the command line parsing to Apache Commons CLI
 */
public class ClParser
{
    @SuppressWarnings("unchecked")

    private Options options = new Options();
    private CommandLineParser parser = new GnuParser();
    private CommandLine line;

    /**
     * Configures the CLI parser with a list of valid Options
     * and parses the command-line args.
     *
     * @param args command-line args passed to main()
     */
    public ClParser( final String[] args ) {
        initOptions();

        if (args == null || args.length == 0) {
            System.err.println( "Error. You need to specify an input file. See following usage guide:" );
            parseArguments( new String[]{ "-help" } );
        } else {
            parseArguments( args );
        }
    }

    /**
     * Parse the args using the Apache Commons CLI parser.
     *
     * @param args arguments from main()
     */
    protected void parseArguments( String[] args ) {
        try {
            String[] preprocessedArgs = preProcessCommandLine( args );
            line = parser.parse( options, preprocessedArgs );
        } catch( ParseException e ) {
            System.err.println( e.getLocalizedMessage() );
            throw new StopExecutionException( e.getLocalizedMessage() );
        }
    }

    /**
     * Set up all valid Platypus options
     */
    protected void initOptions() {
        ClOptions clo = new ClOptions();
        Option[] optsArr = clo.getOptions();

        for( Option opt : optsArr ) {
            options.addOption( opt );
        }
    }

    /**
     * The first two arguments on the command line, if they're not switches
     * are the names of the input and output files respectively. To make it
     * easier to look them up we insert "-infile" and "-outfile" before them.
     * @param args the command-line args
     * @return preprocessed args: the command line w/ insertions made.
     */
    public String[] preProcessCommandLine(final String[] args)
    {
        final List<String> newArgs = new ArrayList<String>();
        boolean inOption = false;
        boolean inputSeen = false;

        for (String arg : args) {
            if (inOption == false) {
                if (isArgAnOption(arg)) {
                    if (doesOptionHaveArg(arg)) {
                        inOption = true;
                    }
                } else if (inputSeen == false) { // we have encountered a bareword
                    newArgs.add("-infile"); // first bareword is taken as input file
                    inputSeen = true;
                } else if (inputSeen == true) {
                    newArgs.add("-outfile"); // next bare word is output file
                    inputSeen = false;
                }
            } else if (inOption == true) {
                inOption = false; // this bare word is an option argument
            }
            newArgs.add(arg);
        }
        return newArgs.toArray(new String[0]); // java collections idiom for converting to an array :-)
    }

    /**
     *  Does the option expect an argument?
     */
    protected boolean doesOptionHaveArg(String option) {
        Option opt = options.getOption( option );
        if( opt != null ) {
            return( opt.hasArg() );
        }
        return( false );
    }

    /**
     * Is the given string an option (that is, does it start with a hyphen)?
     */
    protected boolean isArgAnOption( String arg ) {
        if (( arg != null ) && arg.startsWith( "-" )) {
                return( true );
        }
        return false;
    }

    /**
     * CLI's data structure for all the options and arguments on the command line
     */
    public CommandLine getLine() {
        return( line );
    }

    /**
     * Return all possible options as a sorted TreeMap of option, description
     */
    public TreeMap<String,String> getAllOptions() {
        ClOptions clo = new ClOptions();
        TreeMap<String, String> optionsWithDesc = new TreeMap<String, String>();
        for( Option o: clo.getOptions() ) {
            optionsWithDesc.put( "-" + o.getOpt()  + ( o.hasArg() ? ( " [" + o.getArgName() + "]") : ""),
                                 o.getDescription() );
        }
        return( optionsWithDesc );
    }
}
